﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;

using System.Net;
using System.Security;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Media.Imaging;
using System.Windows.Controls;
using System.Diagnostics;
using System.Security.Cryptography;
using System.IO;
using System.Xml;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows.Media;
using HtmlAgilityPack;
using BBRILCSharp.Commands;
using BBRILCSharp.Commons;
using BBRILCSharp.Models;
using Net.JGLI.RilSharp;


namespace BBRILCSharp.ViewModels
{
    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            // Blank
            LoadSetting();
        }

        private string _filepath = "bb.ril";
        private string _infoseparator = "; ";
        public delegate void MethodInvoker();

        #region Attributes

        private bool _isBusy = false;

        public bool IsBusy
        {
            get { return _isBusy; }
            set
            {
                _isBusy = value;
                OnPropertyChanged("IsBusy");
            }
        }

        private string _busyContent = "就绪";

        public string BusyContent
        {
            get { return _busyContent; }
            set
            {
                _busyContent = value;
                OnPropertyChanged("BusyContent");
            }
        }

        private string _saveDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

        public string SaveDirectory
        {
            get { return _saveDirectory; }
            set
            {
                _saveDirectory = value;
                OnPropertyChanged("SaveDirectory");
            }
        }

        private Net.JGLI.RilSharp.ReadState _rilListType = Net.JGLI.RilSharp.ReadState.All;

        public Net.JGLI.RilSharp.ReadState RilListType
        {
            get { return _rilListType; }
            set
            {
                _rilListType = value;
                OnPropertyChanged("RilListType");
            }
        }

        private AccountModel _accountModel = new AccountModel();

        public AccountModel UserModel
        {
            get { return _accountModel; }
            set
            {
                _accountModel = value;
                OnPropertyChanged("UserModel");
            }
        }

        private RILDataModel _rilModel = new RILDataModel();

        public RILDataModel RilModel
        {
            get { return _rilModel; }
            set
            {
                _rilModel = value;
                OnPropertyChanged("RilModel");
            }
        }


        #endregion

        #region Command
        private DelegateCommand exitCommand;
        public DelegateCommand ExitCommand
        {
            get
            {
                if (exitCommand == null)
                {
                    exitCommand = new DelegateCommand(Exit);
                }
                return exitCommand;
            }
        }

        private void Exit()
        {
            SaveSetting();

            Application.Current.Shutdown();
        }

        private DelegateCommand<object> linkCommand;

        public DelegateCommand<object> LinkCommand
        {
            get
            {
                if (linkCommand == null)
                {
                    linkCommand = new DelegateCommand<object>(OpenLink);
                }
                return linkCommand;
            }
            set { linkCommand = value; }
        }

        private void OpenLink(object param)
        {
            string link = param.ToString();
            Process.Start(link);
        }

        private DelegateCommand<object> logOnCommand;
        public DelegateCommand<object> LogOnCommand
        {
            get
            {
                if (logOnCommand == null)
                {
                    logOnCommand = new DelegateCommand<object>(LogOnPro);
                }
                return logOnCommand;
            }
        }

        private void LogOnPro(object param)
        {
            LogOn();
        }

        private DelegateCommand browserFolderCommand;

        public DelegateCommand BrowserFolderCommand
        {
            get
            {
                if (browserFolderCommand == null)
                {
                    browserFolderCommand = new DelegateCommand(BrowserFolder);
                }
                return browserFolderCommand;
            }
        }

        private void BrowserFolder()
        {
            System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog();
            dlg.SelectedPath = SaveDirectory;
            dlg.Description = "请选择电子书保存的位置。";

            System.Windows.Forms.DialogResult result = dlg.ShowDialog();
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                SaveDirectory = dlg.SelectedPath;
            }
            else
            {

            }
        }

        private DelegateCommand<object> createSNBCommand;

        public DelegateCommand<object> CreateSNBCommand
        {
            get
            {
                if (createSNBCommand == null)
                {
                    createSNBCommand = new DelegateCommand<object>(threadpool_CreateSNB);
                }
                return createSNBCommand;
            }
        }

        private DelegateCommand<object> refreshCommand;

        public DelegateCommand<object> RefreshCommand
        {
            get
            {
                if (refreshCommand == null)
                {
                    refreshCommand = new DelegateCommand<object>(Refresh);
                }
                return refreshCommand;
            }
        }

        private DelegateCommand<object> addPageToListCommand;

        public DelegateCommand<object> AddPageToListCommand
        {
            get
            { 
                if (addPageToListCommand == null)
                {
                    addPageToListCommand = new DelegateCommand<object>(threadpool_AddPageToList);
                }
                return addPageToListCommand;
            }
        }

        #endregion

        #region Function

        public bool LogOn()
        {
            IsBusy = true;
            BusyContent = "正在登录。。。";

            //
            if (Authenticate())
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(GetRILList));
            }
            else
            {
                MessageBox.Show("登陆失败， 请重新输入用户名密码");
            }
            
            BusyContent = "就绪";
            IsBusy = false;
            return true;
        }

        public void LoadSetting()
        {
            try
            {
                FileStream fs = File.Open(_filepath, FileMode.Open);

                byte[] result = new byte[1024];
                UTF8Encoding temp = new UTF8Encoding(true);

                int len = fs.Read(result, 0, result.Length);

                string info = AES.AESDecrypt(temp.GetString(result, 0, len));
                string[] str = info.Split(_infoseparator.ToCharArray());

                //
                UserModel.UserName = str[0];
                UserModel.Password = str[2];
                UserModel.IsRememberMe = bool.Parse(str[4]);
                UserModel.IsAutoLogOn = bool.Parse(str[6]);
                SaveDirectory = str[8];
                RilModel.State = (Net.JGLI.RilSharp.ReadState)Enum.Parse(typeof(Net.JGLI.RilSharp.ReadState), str[10]);
                RilModel.IsIncludeImages = (bool)bool.Parse(str[12]);

                if (!UserModel.IsRememberMe)
                {
                    UserModel.Password = "";
                }
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }

        public void SaveSetting()
        {
            try
            {
                FileStream fs = File.Open(_filepath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);

                if (!UserModel.IsRememberMe)
                {
                    UserModel.Password = " ";
                }

                string info = string.Format("{0:s}{1:s}{2:s}{1:s}{3:s}{1:s}{4:s}{1:s}{5:s}{1:s}{6:s}{1:s}{7:s}",
                    UserModel.UserName,
                    _infoseparator,
                    UserModel.Password,
                    UserModel.IsRememberMe.ToString(),
                    UserModel.IsAutoLogOn.ToString(),
                    SaveDirectory, 
                    RilModel.State.ToString(),
                    RilModel.IsIncludeImages.ToString());

                Byte[] temp = new UTF8Encoding(true).GetBytes(AES.AESEncrypt(info));

                fs.Write(temp, 0, temp.Length);
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }

        public void MakeAsReadOrUnRead(object param)
        {
            RilListItem item = (RilListItem)param;
             
            bool result = RilModel.ReadingList.Contains(item);
            if (result)
            {
                if (item.State == Net.JGLI.RilSharp.ReadState.Unread)
                {
                    item.State = Net.JGLI.RilSharp.ReadState.Read;
                }
                else
                {
                    item.State = Net.JGLI.RilSharp.ReadState.Unread;
                }

                RilModel.ReadingList.Remove(item);
                RilModel.ReadArchiveList.Add(item);
            }
            else
            {
                result = RilModel.ReadArchiveList.Contains(item);
                if (result)
                {
                    if (item.State == Net.JGLI.RilSharp.ReadState.Unread)
                    {
                        item.State = Net.JGLI.RilSharp.ReadState.Read;
                    }
                    else
                    {
                        item.State = Net.JGLI.RilSharp.ReadState.Unread;
                    }

                    RilModel.ReadArchiveList.Remove(item);
                    RilModel.ReadingList.Add(item);
                }
            }

            if (item.state == Net.JGLI.RilSharp.ReadState.Read)
            {
                /*ThreadPool.QueueUserWorkItem(new WaitCallback(MakePageAsRead), item);*/
                MakePageAsRead(item);
            }
        }

        private void Refresh(object param)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(GetRILList));
        }

        private void GetRILList(object param)
        {
            if (App.Current.Dispatcher != null)
            {
                System.Windows.Threading.Dispatcher.FromThread(App.Current.Dispatcher.Thread).Invoke((MethodInvoker)delegate
                {
                    RilModel.ReadArchiveList.Clear();
                    RilModel.ReadingList.Clear();
                    RilModel.AllList.Clear();
                }
                , null);
            }
            else
            {
                RilModel.ReadArchiveList.Clear();
                RilModel.ReadingList.Clear();
                RilModel.AllList.Clear();
            }

            IsBusy = true;
            BusyContent = "正在获取列表。。。";

            RilList readingList = RilModel.RCL.Get(Net.JGLI.RilSharp.ReadState.Unread, null, null, null, false, false);
            RilList readArchive = RilModel.RCL.Get(Net.JGLI.RilSharp.ReadState.Read, null, null, null, false, false);

            if (App.Current.Dispatcher != null)
            {
                System.Windows.Threading.Dispatcher.FromThread(App.Current.Dispatcher.Thread).Invoke((MethodInvoker)delegate
                {
                    if (readingList != null)
                    {
                        foreach (KeyValuePair<string, RilListItem> item in readingList.Items)
                        {
                            RilModel.ReadingList.Add(item.Value);
                            RilModel.AllList.Add(item.Value);
                        }
                    }

                    if (readArchive != null)
                    {
                        foreach (KeyValuePair<string, RilListItem> item in readArchive.Items)
                        {
                            RilModel.ReadArchiveList.Add(item.Value);
                            RilModel.AllList.Add(item.Value);
                        }
                    }
                }
                , null);
            }
            else
            {
                if (readingList != null)
                {
                    foreach (KeyValuePair<string, RilListItem> item in readingList.Items)
                    {
                        RilModel.ReadingList.Add(item.Value);
                        RilModel.AllList.Add(item.Value);
                    }
                }

                if (readArchive != null)
                {
                    foreach (KeyValuePair<string, RilListItem> item in readArchive.Items)
                    {
                        RilModel.ReadArchiveList.Add(item.Value);
                        RilModel.AllList.Add(item.Value);
                    }
                }
            }

            BusyContent = "就绪";
            IsBusy = false;
        }

        private string GetPageContent(RilListItem item)
        {
            try
            {
                object result = RilModel.RCL.Text(item.Url, TextMode.more, true);

                if (result != null)
                {
                    return result.ToString();
                }
            }
            catch (System.Exception ex)
            {
                return "";
            }

            return "";
        }

        private void MakePageAsRead(object item)
        {
            List<RilListItem> itemsToBeMarkedAs  = new List<RilListItem>();

            itemsToBeMarkedAs.Add(item as RilListItem);

            if (!RilModel.RCL.Send(SendType.Read, itemsToBeMarkedAs ))
                throw new Exception("Something went wrong while adding the pages.");
        }

        private bool Authenticate()
        {
            if (RilModel.RCL == null)
            {
                RilModel.RCL = new RilClient(new Credentials
                           {
                               ApiKey = UserModel.ConsumerSecret,
                               UserName = UserModel.UserName,
                               Password = UserModel.Password
                           });
            }

            return RilModel.RCL.Authenticate();
        }

        private void Register()
        {
            RilClient rcl = new RilClient(new Credentials
            {
                ApiKey = UserModel.ConsumerSecret,
                UserName = UserModel.UserName,
                Password = UserModel.Password
            });

            if (!rcl.RegisterClient())
                throw new Exception("There was a problem when registering the new user");
        }

        private void AddMultiPagesToList()
        {
            RilClient rcl = new RilClient(new Credentials
            {
                ApiKey = UserModel.ConsumerSecret,
                UserName = UserModel.UserName,
                Password = UserModel.Password
            });

            List<RilListItem> newItems = new List<RilListItem>(2);

            newItems.Add(new RilListItem
            {
                Url = "http://rilsharp.suddenelfilio.net",
                Title = "Ril#"
            });

            newItems.Add(new RilListItem
            {
                Url = "http://www.google.com",
                Title = "Google"
            });

            ;

            if (!rcl.Send(SendType.New, newItems))
                throw new Exception("Something went wrong while adding the pages.");
        }

        private void threadpool_AddPageToList(object param)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(AddPageToList), param);
        }

        private void AddPageToList(object param)
        {
            bool isAutoTitle = false;

            RilModel.NewPageTitle = RilModel.NewPageTitle.Trim();
            RilModel.NewPageUrl = RilModel.NewPageUrl.Trim();

            if (RilModel.NewPageTitle.Length <= 0)
            {
                isAutoTitle = true;
            }

            if (RilModel.NewPageUrl.Length <= 0)
            {
                MessageBox.Show("请输入网址");
                return;
            }

            if (!RilModel.RCL.Add(RilModel.NewPageUrl, RilModel.NewPageTitle, isAutoTitle)) //true: using auto title; false: using param
                throw new Exception("Something went wrong while adding the page.");

            if (App.Current.Dispatcher != null)
            {
                System.Windows.Threading.Dispatcher.FromThread(App.Current.Dispatcher.Thread).Invoke((MainViewModel.MethodInvoker)delegate
                {
                    Window wnd = param as Window;
                    if (wnd != null)
                    {
                        wnd.Close();
                    }
                }
                , null);
            }
            else
            {
                Window wnd = param as Window;
                if (wnd != null)
                {
                    wnd.Close();
                }
            } 
        }

        private void UpdateTitleOfMultiPages()
        {
            RilClient rcl = new RilClient(new Credentials
            {
                ApiKey = UserModel.ConsumerSecret,
                UserName = UserModel.UserName,
                Password = UserModel.Password
            });

            List<RilListItem> itemsToUpdate = new List<RilListItem>(2);

            itemsToUpdate.Add(new RilListItem
            {
                Url = "http://rilsharp.suddenelfilio.net",
                Title = "Ril#"
            });

            itemsToUpdate.Add(new RilListItem
            {
                Url = "http://www.google.com",
                Title = "Google"
            });

            ;

            if (!rcl.Send(SendType.Update_title, itemsToUpdate))
                throw new Exception("Something went wrong while adding the pages.");
        }

        public string GetHtml(string link)
        {
            var req = (HttpWebRequest)WebRequest.Create(link);
            using (var resp = req.GetResponse().GetResponseStream())
            using (var read = new StreamReader(resp, Encoding.GetEncoding("UTF-8")))
            {
                if (read == null)
                {
                    return "";
                }

                try
                {
                    return read.ReadToEnd();
                }
                catch (System.Exception ex)
                {
                    System.Diagnostics.Trace.WriteLine(ex.Message);
                    return "";
                }
            }
        }

        private void threadpool_CreateSNB(object param)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(CreateSNB), param);
        }

        private void CreateSNB(object param)
        {
            Button btn = param as Button;

            if (App.Current.Dispatcher != null)
            {
                System.Windows.Threading.Dispatcher.FromThread(App.Current.Dispatcher.Thread).Invoke((MainViewModel.MethodInvoker)delegate
                {
                    btn.IsEnabled = false;
                }
                , null);
            }
            else
            {
                btn.IsEnabled = false;
            }

            IsBusy = true;
            BusyContent = "开始转换...";

            ConverterToSNB();

            BusyContent = "就绪";
            IsBusy = false;

            if (App.Current.Dispatcher != null)
            {
                System.Windows.Threading.Dispatcher.FromThread(App.Current.Dispatcher.Thread).Invoke((MainViewModel.MethodInvoker)delegate
                {
                    btn.IsEnabled = true;
                }
                , null);
            }
            else
            {
                btn.IsEnabled = true;
            }
        }

        private void ConverterToSNB()
        {
            ObservableCollection<RilListItem> items = null;

            switch (RilModel.State/*RilListType*/)
            {
                case Net.JGLI.RilSharp.ReadState.Unread:
                    items = RilModel.ReadingList;
                    break;
                case Net.JGLI.RilSharp.ReadState.Read:
                    items = RilModel.ReadArchiveList;
                    break;
                case Net.JGLI.RilSharp.ReadState.All:
                    items = RilModel.AllList;
                    break;
                default:
                    break;
            }

            string bookName = "BB稍后阅读-";
            switch (RilModel.State/*RilListType*/)
            {
                case Net.JGLI.RilSharp.ReadState.Unread:
                    bookName += "未读列表";
                    break;
                case Net.JGLI.RilSharp.ReadState.Read:
                     bookName += "已读列表";
                    break;
                case Net.JGLI.RilSharp.ReadState.All:
                    bookName += "所有列表";
                    break;
                default:
                    break;
            }

            CreateSNBDirectory(bookName);

            CreateBookSNBF(bookName);

            CreateTocSNBF(bookName, items);

            CreateSNBFromItems(items, bookName);

            DeleteTempFiles(bookName);
        }

        private void CreateSNBFromItems(ObservableCollection<RilListItem> items, string bookName)
        {
            string path = bookName;
            if (Directory.Exists(path))
            {
                foreach (RilListItem item in items)
                {
                    string snbc = string.Format("{0:s}.snbc", item.ID.ToString());

                    FileStream file2 = new FileStream(path + "\\snbc\\" + snbc, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
                    StreamWriter writer = new StreamWriter(file2, Encoding.GetEncoding("UTF-8"));

                    string textBegin = string.Format("<?xml version='1.0' encoding='UTF-8'?><snbc><head><title><![CDATA[{0:s}]]></title></head><body>", bookName);
                    string textEnd = "</body></snbc>";
                    string body = "";

                    BusyContent = "正在生成 " + item.Title + " 电子书...";
                   
                    string line = GetHtml(item.Url);

                    //{Text
                    HtmlToText htt = new HtmlToText();
                    string content = htt.StripHTML(line);

                    if (content.Length > 0)
                    {
                        body += @"<text><![CDATA[";
                        body += content;
                        body += @"]]></text>";
                    }
                    //Text} 

                    writer.WriteLine(textBegin + body + textEnd);

                    writer.Close();
                    file2.Close();
                }

                BambookCore.BambookPackSnbFromDir(SaveDirectory + "\\" + bookName + ".snb", bookName);
            }
        }

        private void CreateSNBDirectory(string bookName)
        {
            string appPath = System.IO.Directory.GetCurrentDirectory();
            string path = appPath + "\\" + bookName;

            try
            {
                Directory.CreateDirectory(path);

                Directory.CreateDirectory(path + "\\snbc");
                Directory.CreateDirectory(path + "\\snbc\\images");
                Directory.CreateDirectory(path + "\\snbf");
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        private void CreateBookSNBF(string bookName)
        {
            string str = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + ".book.snbf";
            System.IO.Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(str);

            XmlDocument doc = new XmlDocument();
            doc.Load(stream);

            string path = @"/book-snbf[1]/head[1]/name[1]";
            var name = doc.DocumentElement.SelectSingleNode(path);
            name.InnerText = bookName;

            path = @"/book-snbf[1]/head[1]/author[1]";
            var author = doc.DocumentElement.SelectSingleNode(path);
            author.InnerText = UserModel.UserName;

            path = @"/book-snbf[1]/head[1]/generator[1]";
            var generator = doc.DocumentElement.SelectSingleNode(path);
            generator.InnerText = "Bambook 稍后阅读 V1.01";

            path = @"/book-snbf[1]/head[1]/created[1]";
            var created = doc.DocumentElement.SelectSingleNode(path);
            created.InnerText = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();

            doc.Save(bookName + "\\snbf\\book.snbf");
        }

        private void CreateTocSNBF(string bookName, ObservableCollection<RilListItem> items)
        {
            string str = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + ".toc.snbf";
            System.IO.Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(str);

            XmlDocument doc = new XmlDocument();
            doc.Load(stream);

            string path = @"/toc-snbf[1]/head[1]/chapters[1]";
            var count = doc.DocumentElement.SelectSingleNode(path);

            path = @"/toc-snbf[1]/body[1]";
            var body = doc.DocumentElement.SelectSingleNode(path);

            count.InnerText = items.Count().ToString();

            foreach (var e in items)
            {
                string snbc = string.Format("{0:s}.snbc", e.ID.ToString());

                XmlElement elem = doc.CreateElement("chapter");

                XmlAttribute attr = doc.CreateAttribute("src");
                attr.Value = snbc;

                XmlCDataSection CData;
                CData = doc.CreateCDataSection(e.Title);

                elem.SetAttributeNode(attr);
                elem.AppendChild(CData);

                body.AppendChild(elem);
            }

            doc.Save(bookName + "\\snbf\\toc.snbf");
        }

        private void DeleteTempFiles(string bookName)
        {
            DirectoryInfo dir = new DirectoryInfo(bookName);

            try
            {
                Directory.Delete(bookName, true);
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        #endregion

    }
}
